Skip to content

feat: modernize extension with WXT, TypeScript, and comprehensive testing#4

Merged
justcarlson merged 76 commits intomasterfrom
feat/wxt-modernization-phases-1-4
Jan 25, 2026
Merged

feat: modernize extension with WXT, TypeScript, and comprehensive testing#4
justcarlson merged 76 commits intomasterfrom
feat/wxt-modernization-phases-1-4

Conversation

@justcarlson
Copy link
Copy Markdown
Owner

@justcarlson justcarlson commented Jan 25, 2026

Summary

Major modernization of QuickTab extension from legacy stack (jQuery 1.6.1, ES5, Grunt) to modern tooling (TypeScript, WXT/Vite, Vitest/Playwright). This PR includes Phases 1-4 of the modernization roadmap.

Phase 1: Build Foundation

  • WXT framework with Vite bundler
  • TypeScript with strict mode
  • Biome linting with pre-commit hooks
  • Source maps for debugging

Phase 2: Core Migration

  • Service worker with storage-first architecture (MV3 compliant)
  • URL matching module for Zendesk route detection
  • Chrome API wrappers for storage and tabs

Phase 3: UI Migration

  • Popup with mode toggle (vanilla TypeScript, no jQuery)
  • Welcome page with responsive layout
  • CSS custom properties with automatic dark mode
  • i18n support

Phase 4: Testing & Quality

  • Vitest with 90 unit tests (98.41% coverage)
  • Playwright E2E tests for popup UI
  • GitHub Actions CI workflow (lint, test, build, e2e)
  • Service worker termination tests (TEST-08)

Test plan

  • npm run lint passes
  • npm run test:coverage -- --run passes with 70%+ coverage
  • npm run build produces loadable extension
  • npm run test:e2e passes popup tests
  • Extension loads in Chrome without errors
  • Popup mode toggle works and persists

- STACK.md - Technologies and dependencies
- ARCHITECTURE.md - System design and patterns
- STRUCTURE.md - Directory layout
- CONVENTIONS.md - Code style and patterns
- TESTING.md - Test structure
- INTEGRATIONS.md - External services
- CONCERNS.md - Technical debt and issues
Chrome extension modernization: jQuery/Handlebars removal, TypeScript migration, Vite build system, comprehensive testing, and Chrome Web Store compliance.
Mode: yolo
Depth: comprehensive
Parallelization: enabled
Workflow agents: research=on, plan_check=on, verifier=on
Model profile: quality
Files:
- STACK.md
- FEATURES.md
- ARCHITECTURE.md
- PITFALLS.md
- SUMMARY.md

Key findings:
- Stack: WXT framework with TypeScript, Vite, Vitest, Playwright
- Architecture: Event-driven service workers, storage-first state management
- Critical pitfall: Service worker state loss requires storage-based architecture
45 requirements across 7 categories:
- Build System (6)
- Code Quality (4)
- Testing (8)
- CI/CD (5)
- Web Store Compliance (5)
- Git Workflow (6)
- Code Migration (10)

Plus 6 v2 requirements deferred.
Phases:
1. Build Foundation: WXT/Vite + TypeScript + Biome
2. Core Migration: Service worker + URL matching
3. UI Migration: Popup + welcome to vanilla TS
4. Testing & Quality: Vitest + Playwright + coverage
5. Web Store Compliance: Privacy policy + permissions
6. CI/CD & Automation: GitHub Actions + versioning

Git workflow: feature branches, PR merges, phase tags
All 38 v1 requirements mapped to phases.
Phase 01: Build Foundation
- Implementation decisions documented
- Phase boundary established
Phase 1: Build Foundation
- Standard stack identified (WXT 0.20.x, TypeScript 5.9.x, Biome 2.3.x)
- Architecture patterns documented (entrypoints, config, TypeScript setup)
- Pitfalls catalogued (async main, modules conflict, CommonJS, sourcemaps)
Phase 01: Build Foundation
- 4 plan(s) in 3 wave(s)
- Wave 1: 01-01 (WXT/TS), 01-02 (Biome/Husky) - parallel
- Wave 2: 01-03 (static assets) - depends on 01-01
- Wave 3: 01-04 (verification checkpoint) - depends on all
- Ready for execution
- Replace Grunt/Webpack dependencies with WXT 0.20.13
- Add TypeScript 5.9.3 and @types/chrome
- Set package.json type to module for ESM support
- Create wxt.config.ts with manifest configuration
- Add minimal background.ts entrypoint for WXT prepare
- Generate .wxt/ directory with type definitions

Scripts: dev, build, zip, postinstall
Permissions: webNavigation, tabs, scripting, storage
Host permissions: *.zendesk.com
- Create tsconfig.json extending WXT-generated config
- Enable strict mode for all strict type-checking
- Set target ES2022 for modern JavaScript features
- Add noUncheckedIndexedAccess for safer array/object access
- Add noImplicitOverride for explicit method overrides
- Enable verbatimModuleSyntax for ESM enforcement
- Add biome.json with recommended rules and tab indentation
- Add lint, lint:fix, and format scripts to package.json
- Install @biomejs/biome devDependency
- Format TypeScript files with Biome style
- Add popup entrypoint with HTML, TypeScript, and CSS
- Add welcome page entrypoint with HTML and TypeScript
- All entrypoints are placeholders for Phase 3 UI migration
- Build produces loadable Chrome extension in .output/chrome-mv3/
- Install husky and lint-staged devDependencies
- Add prepare script to initialize Husky
- Add lint-staged config for TypeScript and JSON/HTML/CSS files
- Create .husky/pre-commit hook running lint-staged
- Add biome.json with recommended rules and tab indentation
- Add lint, lint:fix, and format scripts to package.json
- Install @biomejs/biome devDependency
- Format TypeScript/CSS files with Biome style
- Configure exclusions for legacy code, generated files, and planning docs
Tasks completed: 2/2
- Task 1: Configure Biome linter and formatter
- Task 2: Set up Husky and lint-staged for pre-commit hooks

SUMMARY: .planning/phases/01-build-foundation/01-02-SUMMARY.md
- Copy extension icons (16px, 48px, 128px) to public/images/icons/
- Copy i18n messages to public/_locales/en/messages.json
- Add appName and appDescription keys for Chrome i18n
- Update wxt.config.ts with icons and default_locale
- Use __MSG_appName__ for i18n-aware manifest name
- Install rollup-plugin-visualizer for bundle inspection
- Add conditional visualizer plugin in wxt.config.ts (ANALYZE=true)
- Add npm run analyze script for on-demand bundle stats
- Generate treemap visualization with gzip/brotli sizes
Tasks completed: 2/2
- Move static assets to public directory
- Add bundle visualizer for size analysis

SUMMARY: .planning/phases/01-build-foundation/01-03-SUMMARY.md
Tasks completed: 2/2
- Run comprehensive build verification (8 checks passed)
- Complete Phase 1 human verification (approved)

Phase 1 (Build Foundation) complete.
SUMMARY: .planning/phases/01-build-foundation/01-04-SUMMARY.md
- 4/4 plans executed successfully
- All BUILD-01 through BUILD-06 requirements verified
- All QUAL-01 through QUAL-03 requirements verified
- Human verified extension loads in Chrome
Phase 02: Core Migration
- Implementation decisions documented
- Phase boundary established
Phase 2: Core Migration
- Standard stack identified (no new deps - Phase 1 sufficient)
- Storage-first service worker architecture documented
- URL matching TypeScript patterns documented
- Pitfalls catalogued (termination, listener registration, serialization)
Phase 02: Core Migration
- 4 plan(s) in 3 wave(s)
- Wave 1: 02-01, 02-02 (parallel - types/url-matching, storage/tabs wrappers)
- Wave 2: 02-03 (service worker implementation)
- Wave 3: 02-04 (verification checkpoint)
- Ready for execution
- RouteType: 'lotus' | 'ticket' union type
- RouteMatch: interface for matched URL with subdomain, path, type
- UrlDetectionMode: 'allUrls' | 'ticketUrls' | 'noUrls' union
- ZendeskTabInfo: tab tracking interface with subdomain and lastActive
- StorageSchema: chrome.storage.local structure definition
- Implement storage-first architecture with loadState/saveState
- Add URL detection mode get/set with 'allUrls' default
- Add clearState for extension updates
- Silent fallback with console.warn on storage errors per CONTEXT.md
- Use Record<> not Map for JSON serialization

Exports: loadState, saveState, getUrlDetection, setUrlDetection, clearState
- matchZendeskUrl(): Parse URLs with native URL constructor
- buildZendeskUrl(): Reconstruct full Zendesk agent URL from RouteMatch
- isZendeskAgentUrl(): Quick check for agent interface URLs
- Uses pathname only, ignoring query params and hash per CONTEXT.md
- Rejects restricted routes (chat, voice, print)
- Ticket routes take priority over lotus routes
- Implement focusTab with window.focused for window-to-front behavior
- Add updateTabUrl for in-place navigation
- Add closeTab with silent ignore on already-closed
- Add queryZendeskTabs for agent tab discovery
- All operations in try/catch per RESEARCH.md pitfall #3

Exports: focusTab, updateTabUrl, closeTab, queryZendeskTabs
Tasks completed: 2/2
- Task 1: Create shared type definitions
- Task 2: Port URL matching logic to TypeScript

SUMMARY: .planning/phases/02-core-migration/02-01-SUMMARY.md
Tasks completed: 2/2
- Create type-safe storage wrapper
- Create type-safe tabs wrapper

SUMMARY: .planning/phases/02-core-migration/02-02-SUMMARY.md
Use fixed dark text color on bright accent background instead of
theme-dependent --text variable which becomes light in dark mode.
- All 4 plans executed (design tokens, popup, welcome page, verification)
- 5/5 requirements marked complete (MIGR-01, MIGR-02, MIGR-08, MIGR-09, MIGR-10)
- Phase goal verified: jQuery/Handlebars fully replaced with vanilla TypeScript
- One contrast fix applied during verification (fe6c9ee)
Phase 04: Testing & Quality
- Implementation decisions documented
- Phase boundary established
Phase 04: Testing & Quality
- Standard stack identified (Vitest + WxtVitest + Playwright)
- Architecture patterns documented (co-located tests, fixtures)
- Pitfalls catalogued (service worker termination, state leaking)
Phase 04: Testing & Quality
- 6 plan(s) in 4 wave(s)
- Wave 1: test infrastructure setup
- Wave 2: unit tests (URL, storage, tabs, background)
- Wave 3: E2E tests with Playwright
- Wave 4: CI workflow integration
- Ready for execution
Address 5 checker issues:
- 04-04: Remove Task 1 (export from closure impossible), use behavior testing
- 04-06: Add .gitignore to files_modified
- 04-01: Add smoke test verifying fake-browser works (TEST-02)
- 04-04: Revise must_haves to reflect behavior testing approach
- 04-05: Change from Wave 3 to Wave 2 (E2E independent of unit tests)
- 04-06: Change from Wave 4 to Wave 3 (correct dependency calculation)
- Add vitest 4.x and @vitest/coverage-v8 for unit testing
- Add @playwright/test for E2E testing
- Add test, test:coverage, and test:e2e npm scripts
- Configure WxtVitest() plugin for Chrome API mocking
- Set v8 coverage provider with text, HTML, and lcov reporters
- Define 70% global coverage thresholds
- Exclude types.ts and UI entry files from coverage
- Add vitest.setup.ts with fakeBrowser.reset() before each test
- Create storage.test.ts smoke test to verify Chrome API mocking (TEST-02)
- Confirm fake-browser integration works with WxtVitest plugin
Tasks completed: 3/3
- Install test dependencies
- Create Vitest configuration with WXT plugin
- Create test setup with mock reset and smoke test

SUMMARY: .planning/phases/04-testing-quality/04-01-SUMMARY.md
- Configure testDir as ./e2e for test file location
- Single worker with no parallel execution for extension testing
- webServer command ensures extension is built before tests
- CI-specific retries and github reporter
- Test all 5 exported functions: loadState, saveState, getUrlDetection, setUrlDetection, clearState
- Verify default state behavior when storage is empty
- Verify state persistence and retrieval roundtrip
- Test silent fallback on storage errors (no throws)
- Uses fakeBrowser for stateful Chrome API mocking
- Create persistent context with extension loaded via Chrome args
- Wait for service worker event to derive extension ID
- Per RESEARCH.md Pitfall 4: prevents flaky tests from timing
- Test message handlers via storage observation (getStatus, setMode)
- Test storage-first architecture (loadState, saveState, clearState)
- Test findMostRecentTab selection logic through storage patterns
- 22 tests covering behavior without requiring internal exports
- Test matchZendeskUrl for valid URLs, restricted routes, invalid URLs
- Test buildZendeskUrl for URL construction
- Test isZendeskAgentUrl for agent vs non-agent URL detection
- 41 tests covering subdomain extraction, path normalization, query/hash handling
- 96.55% line coverage, 100% function coverage on url-matching.ts
- Test all 4 exported functions: focusTab, updateTabUrl, closeTab, queryZendeskTabs
- Verify focusTab activates tab and brings window to front
- Verify updateTabUrl returns boolean success for caller cleanup
- Test silent error handling when tabs don't exist
- Uses vi.spyOn for Chrome tabs API mocking
- Add termination/restart cycle simulation test
- Add in-memory state leakage verification test
- Add rapid restart cycle stress test
- Tests verify storage-first architecture under MV3 constraints
Tasks completed: 2/2
- Task 1: Create URL matching test file with comprehensive coverage
- Task 2: Verify edge cases and coverage gaps (verification pass)

SUMMARY: .planning/phases/04-testing-quality/04-02-SUMMARY.md
Tasks completed: 3/3
- Create storage module unit tests (16 tests, 100% coverage)
- Create tabs module unit tests (11 tests, 100% coverage)
- Verify coverage thresholds (both modules at 100%)

SUMMARY: .planning/phases/04-testing-quality/04-03-SUMMARY.md
Tasks completed: 2/2
- Task 1: Create background service worker unit tests
- Task 2: Test service worker state persistence (TEST-08)

SUMMARY: .planning/phases/04-testing-quality/04-04-SUMMARY.md
- Add popup.spec.ts with 3 tests for mode toggle UI
- Test popup loads with three mode options
- Test mode change persists via clicking labels
- Add mock-pages/agent-ticket.html for future navigation tests
- Move background.test.ts from entrypoints/ to src/ (WXT conflict)
Tasks completed: 3/3
- Playwright configuration for extension testing
- Extension fixtures with service worker waiting
- Popup UI E2E tests (3 tests passing)

SUMMARY: .planning/phases/04-testing-quality/04-05-SUMMARY.md
- Add lint job running Biome checks on PRs (QUAL-04)
- Add test job running unit tests with coverage
- Add build job with artifact upload
- Add e2e job running Playwright tests after build
- Upload coverage, build, and Playwright reports as artifacts
- Add coverage/, playwright-report/, test-results/ to biome excludes
- Fix noNonNullAssertion lint errors in welcome/main.ts with setText helper
- Exclude entrypoints/background.ts from coverage (tested via behavior tests & E2E)

All tests pass locally: lint, unit tests (98.41% coverage), build, E2E.
- Add coverage/ directory (Vitest coverage reports)
- Add playwright-report/ directory (Playwright HTML reports)
- Add test-results/ directory (Playwright test artifacts)
Tasks completed: 3/3
- Create GitHub Actions CI workflow
- Verify all tests pass locally
- Add .gitignore entries for test artifacts

SUMMARY: .planning/phases/04-testing-quality/04-06-SUMMARY.md

Phase 4 (Testing & Quality) complete - 90 unit tests, 3 E2E tests, CI gates.
@justcarlson justcarlson merged commit 886d5a3 into master Jan 25, 2026
4 checks passed
@justcarlson justcarlson deleted the feat/wxt-modernization-phases-1-4 branch January 25, 2026 22:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant